﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using SmartProject.BusinessObjects;

namespace SmartProject.BusinessRules
{
    public class SprintPreConditions : BusinessRule
    {
        public override string Name
        {
            get { return "Sprint assignment pre-conditions."; }
        }

        public override string Description
        {
            get { return "This rule specifies the pre-conditions that applies before a Sprint cycle can be assigned to a work item."; }
        }

        public override void Implement(Entity data)
        {
            // Make sure developer id is already entered before sprint id is also
            // set. In the case of the stored proc smartproject_AddWork and 
            // smartproject_UpdateWork, both these carry Developer Id and Sprint Id.
            if (data.StoredProc == "smartproject_AddWork" ||
                data.StoredProc == "smartproject_UpdateWork")
            {
                // If sprint id is set, developer id must also be set.
                Data.DataParameter sprintIdDataParameter = data.GetDataParameter("SprintId", false);
                if (sprintIdDataParameter.IsDirty)
                {
                    Data.DataParameter developerIdDataParameter = data.GetDataParameter("DeveloperId", false);
                    if (!developerIdDataParameter.IsDirty)
                    {
                        throw new Exceptions.BusinessRuleViolation(
                            "Sprint cycle cannot be set unless you have selected a developer.");
                    }
                }
            }
            else if (data.StoredProc == "smartProject_UpdateWorkSprint")
            {
                // Lookup work.
                int sprintId = Convert.ToInt32(data.GetAttributeValue("SprintId"));
                int workId = Convert.ToInt32(data.GetAttributeValue("WorkId"));
                BusinessObjects.Work work = new SmartProject.BusinessObjects.Work();
                work.SetAttribute("WorkId", workId);
                work.Load();

                int developerId = Convert.ToInt32(work.GetAttributeValue("DeveloperId"));
                if (developerId <= 0)
                {
                    throw new Exceptions.BusinessRuleViolation(
                        "You must select a developer first.");
                }

                int estimatedTime;
                int estimatedTimeTypeId;
                // Check if we already have an estimated time to change.
                if (data.GetAttributeValue("EstTime") == null)
                {
                    if (work.GetAttributeValue("EstTime") == null)
                    {
                        throw new Exceptions.BusinessRuleViolation("You must provide an estimated time.");
                    }
                    else
                    {
                        estimatedTime = (int)work.GetAttributeValue("EstTime");
                        estimatedTimeTypeId = (int)work.GetAttributeValue("EstTimeTypeId");
                    }
                }
                else
                {
                    estimatedTime = (int)data.GetAttributeValue("EstTime");
                    estimatedTimeTypeId = (int)data.GetAttributeValue("EstTimeTypeId");
                }

                DateTime due = work.GetAttributeDateTime("DueDate");
                Sprint sprint = new Sprint();
                sprint.SetAttribute("SprintId", sprintId);
                sprint.Load();

                DateTime start = sprint.GetAttributeDateTime("Started");
                int estSprintDays = (int)sprint.GetAttributeValue("EstimatedDays");
                DateTime estEnd = start.AddDays(estSprintDays);

                if (due > estEnd)
                {
                    throw new Exceptions.BusinessRuleViolation(GetConsiderations());
                }

                Preference preference = new Preference();
                preference.GetValue("DailyWorkHours");
                int workHoursPerDay = Convert.ToInt32(preference.Value);
                int estSprintMins = estSprintDays * workHoursPerDay * 60;
                TimeType timeType = new TimeType();
                
                switch (timeType.GetValue(estimatedTimeTypeId))
                {
                    case "Minute(s)":
                        // Do nothing
                        break;

                    case "Hour(s)":
                        estimatedTime = estimatedTime * 60;
                        break;

                    case "Day(s)":
                        estimatedTime = estimatedTime * workHoursPerDay;
                        break;
                }

                if (estimatedTime > estSprintMins)
                {

                    throw new Exceptions.BusinessRuleViolation(GetConsiderations());
                }
            }
        }

        private string GetConsiderations()
        {
            System.Text.StringBuilder message = new System.Text.StringBuilder();
            message.Append("Work cannot be completed before Sprint ends. Consider the following option(s):<br/><ul>");
            message.Append("<li>changing the Due-date of the work</li>");
            message.Append("<li>changing the End-date of the Sprint.</li>");
            message.Append("<li>breaking work into smaller work items that will fit within this Sprint cycle.</li>");
            message.Append("<li>moving the Work to be in another Sprint Cycle.</li></ul>");
            return message.ToString();
        }

        public override bool QueryBooleanProperty(string propertyName)
        {
            return NotQueryable;
        }
    }
}
